#include<iostream>
#include<queue>
#include<pthread.h>
using namespace std;

const int default_thread = 5; //默认创建多少个线程
const int default_task = 3;//最多有多少个任务
template<class T>
class ThreadPool
{
private:
  ThreadPool(int threadCount = default_thread,int taskCount = default_task)
    :thread_count(threadCount),task_count(taskCount),exit_flag(0)
  {
    pthread_mutex_init(&_mtx,nullptr);
    pthread_cond_init(&cons_cond,nullptr);
    pthread_cond_init(&prod_cond,nullptr);
  }
  ThreadPool(const ThreadPool<T>&) = delete;
  ThreadPool<T>& operator=(const ThreadPool<T>&) = delete;
public:
  ~ThreadPool()
  {
    pthread_mutex_destroy(&_mtx);
    pthread_cond_destroy(&cons_cond);
    pthread_cond_destroy(&prod_cond);
  }
 static ThreadPool<T>* GetInstance()
 {
   //由于是静态的锁 所以可以静态分配初始化,不用手动销毁
   static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; //防止第一次调用该函数的时候,有线程不安全的问题
   if(ins == nullptr)
   {
     pthread_mutex_lock(&lock);
     if(ins == nullptr)
     {
        ins = new ThreadPool<T>();
        cout <<"首次加载单例对象..." << endl;
        int n = ins->InitThreadPool(); //初始化线程池
        if(n <= 0)
        {
          cout <<"ThreadPool Init Fail" << endl;
          exit(1);
        }
        cout <<"ThreadPool Init Success !" << endl;
     }
     pthread_mutex_unlock(&lock);
   }
   return ins;
 }
 static void* Routine(void* arg)
 {
    ThreadPool<T>* tp = (ThreadPool<T>*)arg;
    while(1)
    {
      //访问临界资源前先加锁
      pthread_mutex_lock(&tp->_mtx);
      while(tp->isEmpty()) //任务队列为空->此时已经没有任务了
      {
        //判断退出标志位是否被设置,如果被设置了,该线程就需要退出
        if(tp->exit_flag)
        {
          cout <<pthread_self() <<" exit...." << endl;
          tp->thread_count--;//线程池线程个数-1
          pthread_mutex_unlock(&tp->_mtx);//记得解锁！！！
          pthread_exit(nullptr) ;//终止当前线程
        }
        //消费者需要在条件变量下等待
        pthread_cond_wait(&tp->cons_cond,&tp->_mtx);
      }
      //此时被唤醒 & 任务队列有任务了
      T task;
      tp->pop(&task);
      // 先解锁再处理任务
      pthread_mutex_unlock(&tp->_mtx);
      task(); //task.operator() ==> task.Run();
    }
 }
 int InitThreadPool() //返回值：成功创建的线程个数
 {
    int exit_thread = 0;//有多少个线程创建失败
    for(int i = 0;i<thread_count;i++)
    {
      pthread_t tid;
      int ret = pthread_create(&tid,nullptr,Routine,(void*)this);  
      if(ret < 0)
      {
        exit_thread++;
      }
    }
    thread_count -= exit_thread; //更新线程池个数
    return thread_count;
 }
 //当最后任务执行完之后,销毁线程池 -> 本质是将退出标志位置为1
 //唤醒所有在条件变量等待的消费者线程(线程池当中的线程),如果有任务则先去处理
 //当所有的任务都处理完了,任务队列为空。进入while循环的时候,首先会判断退出标志位是否被设置,如果被设置,则当前线程会退出
 //对于外部线程想要push任务,会先判断退出标志位是否被设置,如果被设置,则不能继续push任务,直接return
 void ThreadPoolExit()
 {
    exit_flag = 1;
    //方式1:
    while(thread_count > 0)
    {
      pthread_cond_signal(&cons_cond);
    }
    //方式2:
   // pthread_cond_broadcast(&cons_cond);
 }
 void push(const T& task)
 {
  pthread_mutex_lock(&_mtx);
  while(task_queue.size() >= task_count) //任务队列的长度>= 最多有多少个任务
  {
     //首先判断退出标志位是否被设置,如果被设置了,则不能插入任务
    if(exit_flag )
    {
      cout <<"exit_flag == 1 !! can't push task" <<endl;
      pthread_mutex_unlock(&_mtx);
      return ;
    }
    //否则:此时任务队列满了,生产者需要在条件变量下等待
    pthread_cond_wait(&prod_cond,&_mtx);
  }
  //此时可以放入任务了
  task_queue.push(task);
  //此时可能有消费者正在等待,所以唤醒消费者进行消费
  //先解锁 or 先唤醒都可以
  pthread_cond_signal(&cons_cond);
  pthread_mutex_unlock(&_mtx);
 }
 void pop(T* task)
 {
  //由于是在加锁的上下文当中,所以可以直接拿任务
  *task = task_queue.front();
  task_queue.pop();
 }
 bool isEmpty()
 {
   return task_queue.empty();
 }
private:
  static ThreadPool<T>* ins ; //单例模式-懒汉模式,指向全局唯一的线程池单例对象
  queue<T> task_queue;
  int exit_flag; //是否退出
  pthread_mutex_t _mtx;//保护任务队列
  pthread_cond_t cons_cond; //当任务队列为空的时候,消费者(线程池当中的线程)要在条件变量下等待
  pthread_cond_t prod_cond; //当任务队列满了的时候,生产者(外部线程)要在条件变量下等待
  int thread_count; //线程池的线程个数
  int task_count;//任务队列的最大任务个数
};
//静态成员在类外初始化
template<class T>
ThreadPool<T>* ThreadPool<T>::ins =  nullptr;
